/*
 * User: ronma
 * Date: 22/04/2008
 *
 * Copyright (2005) IDI. All rights reserved.
 * This software is a proprietary information of Israeli Direct Insurance.
 * Created by IntelliJ IDEA.
 */
package com.idi.maven.plugins.servers;

import com.idi.maven.plugins.BuildProperties;
import com.idi.maven.plugins.BuildPropertiesFactory;
import com.idi.maven.plugins.DeploymentHelper;
import com.idi.remote.assistant.RemoteAccessFacade;
import com.idi.remote.assistant.SshExecClient;
import org.apache.maven.plugin.MojoExecutionException;
import org.jfrog.jade.plugins.common.injectable.MvnInjectable;
import org.jfrog.jade.plugins.common.injectable.MvnInjectableSupport;

import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static com.idi.maven.plugins.DeploymentHelper.JBossCluster_dir_prefix;
import static java.lang.String.format;

/**
 *
 */
public abstract class JndiServer extends MvnInjectableSupport implements Server {
    private final String jndiServer;
    private final boolean isNew;

    private final BuildProperties serverProperties;
    private final DeploymentHelper deploymentHelper;
    private final RemoteAccessFacade remoteAccessFacade;

    protected JndiServer(String jndiServer, boolean isNew) {
        this.jndiServer = jndiServer;
        this.isNew = isNew;

        serverProperties = BuildPropertiesFactory.createBuildPropertiesForJndiServer(jndiServer);
        remoteAccessFacade = new RemoteAccessFacade(getLog(), serverProperties);

        if (isNew() && !serverProperties.isVersioningSupported()) {
            throw new RuntimeException(format("server %s is not a versioned server", jndiServer));
        }

        deploymentHelper = new DeploymentHelper(serverProperties, jndiServer);
    }

    @Override
    public void updateFromMvnInjectable(MvnInjectable injectable) {
        super.updateFromMvnInjectable(injectable);
        deploymentHelper.updateFromMvnInjectable(this);
        remoteAccessFacade.setLog(getLog());
    }

    public void start() throws MojoExecutionException {
        executeOnAllServerHosts(serverProperties.getRestartScriptDir() + "/" + "StartServer" + getServerDirPostfix());

        //todo mamo: should wait & verify that servers are really up?
    }

    public void startTerracotta() throws MojoExecutionException {
        executeOnAllTerracottaServerHosts(serverProperties.getRestartScriptDir() + "/" + "StartTCServer" + getServerDirPostfix());
    }

    public void stop() throws MojoExecutionException {
        executeOnAllServerHosts(serverProperties.getRestartScriptDir() + "/" + "StopServer" + getServerDirPostfix());

        //todo mamo: verify servers are really down
    }

    public void terracotta() throws MojoExecutionException {
        if (deploymentHelper.isTerracottaProvided()) {
            stopTerracotta();
            removeTerracottaDataDirectory();
            startTerracotta();
        }
    }

    protected void removeTerracottaDataDirectory() throws MojoExecutionException {
        String dataDirectoryPath = new JndiServerNew(jndiServer, this).getTerracottaServerDataRootDir();
        for (final String host : deploymentHelper.getAllTerracottaServerHosts()) {
            remoteAccessFacade.executeRemoteCommand(format("rm -rf  %s/*", dataDirectoryPath), host);
        }
        getLog().info("successfully removed data directory");
    }

    public void stopTerracotta() throws MojoExecutionException {
        executeOnAllTerracottaServerHosts(serverProperties.getRestartScriptDir() + "/" + "StopTCServer" + getServerDirPostfix());
    }

    public void status() throws MojoExecutionException {

        //show server's statuses
        Integer ifcNum = getServerDirPostfix().length() == 0 ? 1 : 2;
        List<String> serverInterfaces = Arrays.asList(serverProperties.get(format("node.network.interfaces.%d", ifcNum)).split(","));

        for (String serverInterface : serverInterfaces) {
            getLog().info(format("server %s %s on %s is %s", jndiServer, getVersionName(), serverInterface, (isJndiPortUp(serverInterface) ? "up" : "down")));
        }

        //todo show idi version on each server
    }

    public List<ServerDeployTypeEnum> getSupportedDeploys() {
        List<ServerDeployTypeEnum> supportedDeploys = new ArrayList<ServerDeployTypeEnum>();
        List<String> stringList = Arrays.asList(serverProperties.getSupportedDeploys().split(";"));

        for (final String s : stringList) {
            supportedDeploys.add(ServerDeployTypeEnum.resloveDeployType(s));
        }
        return supportedDeploys;
    }

    public ServerTypeEnum getServerType() {
        return deploymentHelper.getServerType();
    }

//    private void checkIfServersUpAccordingToServerPIDFile() throws MojoExecutionException {
//        final String serverRootDir = getServerRootDir();
//        //cat /opt/jboss/$SERVER_NAME/$JBOSS_DIR/bin/server.pid | xargs pgrep -P | wc -l
//        for (final String host : deploymentHelper.getAllServerHosts()) {
//            final StringOutputStream stream = new StringOutputStream();
//            SshExecClient sshExecClient = new SshExecClient(host, serverProperties, 200000) {
//                protected OutputStream getOutputStreamDelegator() {return stream;}
//            };
//            sshExecClient.execute(format("cat %s/bin/server.pid | xargs pgrep -P | wc -l", serverRootDir));
//            final boolean isUp = !stream.toString().trim().startsWith("0'");
//            log.info(format("server %s %s on %s is %s", jndiServer, serverVersion, host, (isUp ? "up" : "down")));
//        }
//    }

    private boolean isJndiPortUp(String hostInterface) {
        try {
            new Socket(hostInterface, 1099).close();
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    //helper methods

    /**
     * return either /opt/jboss/HEAD/JBossCluster or /opt/jboss/HEAD/JBossClusterSecond, depending on the version
     * locally cached
     */
    private String serverRootDir;

    public String getServerRootDir() throws MojoExecutionException {
        if (serverRootDir == null) {
            if (!serverProperties.isVersioningSupported()) {
                //no version at all, return the first (and only) dir
                serverRootDir = serverProperties.getFirstEarRootDir();
            } else {
                //fetch current version and resolve containing directory
                int currentVersion = deploymentHelper.getCurrentVersion();
                String currentEarDir = getEarDirWithVersion(currentVersion);

                //current dir fallback is first ear dir
                if (currentEarDir == null) {
                    currentEarDir = serverProperties.getFirstEarRootDir();
                }

                if (isCurrent()) {
                    serverRootDir = currentEarDir;
                } else if (isNew()) {
                    //second dir is the other ear dir that is not current
                    serverRootDir = currentEarDir.equals(serverProperties.getFirstEarRootDir()) ? serverProperties.getSecondEarRootDir() : serverProperties.getFirstEarRootDir();
                }
            }
        }
        return serverRootDir;
    }

    /**
     * get the deploy directory that contains idiX.ear where version is X
     *
     * @param version the version to look for
     * @return the directory that contains the version, otherwise null
     * @throws MojoExecutionException
     */
    private String getEarDirWithVersion(Integer version) throws MojoExecutionException {
        String earDir = null;
        Integer versionOnFirst = deploymentHelper.getEarVersionOn(serverProperties.getServerHost(), serverProperties.getFirstEarDeployDir());
        Integer versionOnSecond = deploymentHelper.getEarVersionOn(serverProperties.getServerHost(), serverProperties.getSecondEarDeployDir());

        if (version.equals(versionOnFirst)) {
            earDir = serverProperties.getFirstEarRootDir();
        } else if (version.equals(versionOnSecond)) {
            earDir = serverProperties.getSecondEarRootDir();
        }
        //implicit: if versionOnFirst == versionOnSecond == version than prefer first ear directory

        return earDir;
    }

    /**
     * return either /opt/jboss/HEAD/JBossCluster or /opt/jboss/HEAD/JBossClusterSecond, depending on the version
     * locally cached
     */
    private String terracottaServerDataRootDir;

    public String getTerracottaServerDataRootDir() throws MojoExecutionException {
        if (terracottaServerDataRootDir == null) {
            if (!serverProperties.isVersioningSupported()) {
                //no version at all, return the first (and only) dir
                terracottaServerDataRootDir = serverProperties.getFirstTerracottaDataRootDir();
            } else {
                //fetch current version and resolve containing directory
                int currentVersion = deploymentHelper.getCurrentVersion();
                String currentTerracottaDir = getTerracottaDirWithVersion(currentVersion);
                //current dir fallback is first terracotta dir
                if (currentTerracottaDir == null) {
                    currentTerracottaDir = serverProperties.getFirstTerracottaDataRootDir();
                }
                if (isCurrent()) {
                    terracottaServerDataRootDir = currentTerracottaDir;
                } else if (isNew()) {
                    //second dir is the other terracotta dir that is not current
                    terracottaServerDataRootDir = currentTerracottaDir.equals(serverProperties.getFirstTerracottaDataRootDir()) ? serverProperties.getSecondTerracottaDataRootDir() : serverProperties.getFirstTerracottaDataRootDir();
                }
            }
        }
        return terracottaServerDataRootDir;
    }

    /**
     * get the deploy directory that contains idiX.ear where version is X
     *
     * @param version the version to look for
     * @return the directory that contains the version, otherwise null
     * @throws MojoExecutionException
     */
    private String getTerracottaDirWithVersion(Integer version) throws MojoExecutionException {
        String terracottaDir = null;
        Integer versionOnFirst = deploymentHelper.getEarVersionOn(serverProperties.getServerHost(), serverProperties.getFirstEarDeployDir());
        Integer versionOnSecond = deploymentHelper.getEarVersionOn(serverProperties.getServerHost(), serverProperties.getSecondEarDeployDir());

        if (version.equals(versionOnFirst)) {
            terracottaDir = serverProperties.getFirstTerracottaDataRootDir();
        } else if (version.equals(versionOnSecond)) {
            terracottaDir = serverProperties.getSecondTerracottaDataRootDir();
        }
        //implicit: if versionOnFirst == versionOnSecond == version than prefer first terracottaDir directory

        return terracottaDir;
    }

    public String getServerDeployDir() throws MojoExecutionException {
        return getServerRootDir() + "/" + serverProperties.getEarRelativeDeployDir();
    }

    /**
     * for StartServer / StartServerSecond (postfix: "" / Second)
     * according to the server's root dir
     */
    private String getServerDirPostfix() throws MojoExecutionException {
        String earDir = getServerRootDir();
        return earDir.substring(earDir.indexOf(JBossCluster_dir_prefix) + JBossCluster_dir_prefix.length());
    }

    public void executeOnAllServerHosts(String command) throws MojoExecutionException {
        for (String host : deploymentHelper.getAllServerHosts()) {
            SshExecClient sshExecClient = remoteAccessFacade.getSshExec(host);
            sshExecClient.setTimeout(200000);
            getLog().info(String.format("executing %s on %s", command, host));
            sshExecClient.execute(command);
        }
    }

    public void executeOnAllTerracottaServerHosts(String command) throws MojoExecutionException {
        for (String host : deploymentHelper.getAllTerracottaServerHosts()) {
            SshExecClient sshExecClient = remoteAccessFacade.getSshExec(host);
            sshExecClient.setTimeout(200000);
            getLog().info(String.format("executing %s on %s", command, host));
            sshExecClient.execute(command);
        }
    }

    public boolean isNew() {
        return isNew;
    }

    public boolean isCurrent() {
        return !isNew;
    }

    private String getVersionName() {
        return isNew ? "new" : "current";
    }

}